#pragma once

#include <iostream>
#include"add_contact.pb.h"
#include"del_contact.pb.h"
#include"find_all_contacts.pb.h"
#include"find_one_contact.pb.h"
#include"httplib.h"
#include"ContactException.h"


using namespace std;
using namespace httplib;
using namespace find_one_contact;
using namespace find_all_contacts;
using namespace add_contact;
using namespace del_contact;

class ContactsClient 
{
public:
    void addContact(const std::string& server_ip, const std::string& server_port);
    void deleteContact(const std::string& server_ip, const std::string& server_port);
    void findContact(const std::string& server_ip, const std::string& server_port);
    void findContacts(const std::string& server_ip, const std::string& server_port);
private:
    void BuildAddRequest(AddRequest* req);
    void printFindOneContactResponse(FindOneContactResponse& resp);
    void printFindAllContactsResponse(FindAllContactsResponse& resp);
};

void ContactsClient::BuildAddRequest(AddRequest* req)
{
    cout << "-------------新增联系⼈-------------" << endl;
    cout << "请输入联系⼈的姓名：";
    string name;
    getline(cin, name);
    req->set_name(name);

    cout << "请输入联系⼈年龄：";
    int age;
    cin >> age;
    req->set_age(age);
    //前面用了 cin >> age; 读取一个整数后，用户输入时最后按下的回车键（\n）仍然留在输入缓冲区。
    //如果不清理掉这个残留的换行符，下一次使用 getline(cin, str) 等读取整行字符串的操作时，会直接读到这个换行符，导致读取失败或行为异常。
    //丢弃（忽略）输入缓冲区中最多 256 个字符，直到遇到换行符 '\n' 为止。
    cin.ignore(256, '\n');

    for(int i = 0; ;i++)
    {
        cout << "请输入联系⼈的电话号码 " << i+1 << "(输入回车退出): ";
        string number;
        getline(cin, number);
        if(number.empty()) break;
        AddRequest_Phone* phone = req->add_phone();
        phone->set_number(number);
        cout << "请输入联系⼈的电话类型" << "(1:移动 2:固定): ";
        int type;
        cin >> type;
        cin.ignore(256, '\n');
        switch (type) 
        {
            case 1:
                phone->set_type(AddRequest_Phone_PhoneType_M_P);    
                break;
            case 2:
                phone->set_type(AddRequest_Phone_PhoneType_TEL_P);
                break;
            default:
                cout << "输入错误" << endl;
                break;
        }
    }
}

void ContactsClient::printFindOneContactResponse(FindOneContactResponse& resp)
{
    cout << "联系人姓名：" << resp.name() << endl;
    cout << "联系人年龄：" << resp.age() << endl;
    for(int i = 0; i < resp.phone_size(); i++)
    {
        FindOneContactResponse_Phone phone = resp.phone(i);
        cout << "联系人电话" << i + 1 << ": " << phone.number();
        cout <<  "     (" << FindOneContactResponse_Phone_PhoneType_Name(phone.type()) << ")" << endl;
    }
}

void ContactsClient::printFindAllContactsResponse(FindAllContactsResponse& resp)
{
    if(resp.contacts_size() == 0)
    {
        cout << "联系人列表为空！" << endl;
        return;
    }
    for(auto it = resp.contacts().begin(); it!= resp.contacts().end(); it++)
    {
        cout << "联系人ID：" << it->uid() << endl;
        cout << "联系人姓名：" << it->name() << endl;
        for(int i = 0; i < it->phone_size(); i++)
        {
            PeopleInfo_Phone phone = it->phone(i);
            cout << "联系人电话" << i + 1 << ": " << phone.number();
        }
    }
}


void ContactsClient::addContact(const std::string& server_ip, const std::string& server_port)
{
    Client cli(server_ip, stoi(server_port));

    //构造req请求
    AddRequest req;
    BuildAddRequest(&req);

    //序列化req请求
    std::string req_str;
    if(!req.SerializeToString(&req_str)) {throw ContactException("序列化请求失败");}

    //发送post请求
    auto res = cli.Post("/contacts/add", req_str, "application/protobuf");
    if(res == nullptr)
    {
        string err_msg;
        err_msg.append("/contacts/add 链接错误！错误信息：")
                .append(httplib::to_string(res.error()));
        throw ContactException(err_msg);
    }

    //反序列化resp响应
    AddResponse resp;
    bool parse = resp.ParseFromString(res->body);
    
    //处理resp异常响应
    if(res->status != 200 && !parse)
    {
        string err_msg;
        err_msg.append("/contacts/add 响应解析错误")
                .append(to_string(res->status))
                .append("(").append(res->reason).append(")");
        throw ContactException(err_msg);
    }
    else if(res->status != 200)
    {
        string err_msg;
        err_msg.append("/contacts/add 响应错误")
                .append(to_string(res->status))
                .append("(").append(res->reason).append("),错误原因：")
                .append(resp.errmessage());
        throw ContactException(err_msg);
    }
    else if(!resp.success())
    {
        string err_msg;
        err_msg.append("/contacts/add结果异常： ")
                .append(resp.errmessage());
        throw ContactException(err_msg);
    }

    //结果打印
    cout << "添加联系人成功！联系人ID：" << resp.uid() << endl;

}

void ContactsClient::deleteContact(const std::string& server_ip, const std::string& server_port)
{
    Client cli(server_ip, stoi(server_port));

    //构造req请求
    DelRequest req;
    string uid;
    cout << "请输入要删除的联系人ID：";
    getline(cin, uid);
    req.set_uid(uid);

    //序列化req请求
    std::string req_str;
    if(!req.SerializeToString(&req_str)) {throw ContactException("序列化请求失败");}

    //发送post请求
    auto res = cli.Post("/contacts/del", req_str, "application/protobuf");
    if(res == nullptr)
    {
        string err_msg;
        err_msg.append("/contacts/del 链接错误！错误信息：")
                .append(httplib::to_string(res.error()));
        throw ContactException(err_msg);
    }

    //反序列化resp响应
    DelResponse resp;
    bool parse = resp.ParseFromString(res->body);
    
    //处理resp异常响应
    if(res->status != 200 && !parse)
    {
        string err_msg;
        err_msg.append("/contacts/del 响应解析错误")
                .append(to_string(res->status))
                .append("(").append(res->reason).append(")");
        throw ContactException(err_msg);    
    }
    else if(res->status != 200)
    {
        string err_msg;
        err_msg.append("/contacts/del 响应错误")
                .append(to_string(res->status))
                .append("(").append(res->reason).append("),错误原因：")
                .append(resp.errmessage());
        throw ContactException(err_msg);
    }
    else if(!resp.success())
    {            
        string err_msg;
        err_msg.append("/contacts/del结果异常： ")       
                .append(resp.errmessage());
        throw ContactException(err_msg);
    }

    //结果打印
    cout << "删除联系人成功！联系人ID：" << uid << endl;
}

void ContactsClient::findContact(const std::string& server_ip, const std::string& server_port)
{
    Client cli(server_ip, stoi(server_port));

    //构造req请求
    FindOneContactRequest req;
    string uid;
    cout << "请输入要查找的联系人ID：";
    getline(cin, uid);
    req.set_uid(uid);

    //序列化req请求           
    std::string req_str;
    if(!req.SerializeToString(&req_str)) {throw ContactException("序列化请求失败");}

    //发送post请求
    auto res = cli.Post("/contacts/find_one", req_str, "application/protobuf");
    if(res == nullptr)
    {
        string err_msg;
        err_msg.append("/contacts/find_one 链接错误！错误信息：")
                .append(httplib::to_string(res.error()));
        throw ContactException(err_msg);
    }

    //反序列化resp响应
    FindOneContactResponse resp;
    bool parse = resp.ParseFromString(res->body);
    
    //处理resp异常响应
    if(res->status != 200 && !parse)
    {
        string err_msg;
        err_msg.append("/contacts/find_one 响应解析错误")
                .append(to_string(res->status))
                .append("(").append(res->reason).append(")");
        throw ContactException(err_msg);
    }    
    else if(res->status != 200)
    {
        string err_msg;
        err_msg.append("/contacts/find_one 响应错误")
                .append(to_string(res->status))
                .append("(").append(res->reason).append("),错误原因：")
                .append(resp.errmessage());
        throw ContactException(err_msg);
    }
    else if(!resp.success())
    {            
        string err_msg;        
        err_msg.append("/contacts/find_one结果异常： ")       
                .append(resp.errmessage());
        throw ContactException(err_msg);
    }

    //结果打印
    printFindOneContactResponse(resp);
}

void ContactsClient::findContacts(const std::string& server_ip, const std::string& server_port)
{
    Client cli(server_ip, stoi(server_port));

    //发起get请求
    auto res = cli.Get("/contacts/find_all");
    if(res == nullptr)
    {
        string err_msg;
        err_msg.append("/contacts/find_all 链接错误！错误信息：")
                .append(httplib::to_string(res.error()));
        throw ContactException(err_msg);
    }

    //反序列化resp响应
    FindAllContactsResponse resp;
    bool parse = resp.ParseFromString(res->body);
    
    //处理resp异常响应
    if(res->status != 200 && !parse)
    {
        string err_msg;
        err_msg.append("/contacts/find_all 响应解析错误")
                .append(to_string(res->status))
                .append("(").append(res->reason).append(")");
        throw ContactException(err_msg);
    }
    else if(res->status != 200)
    {
        string err_msg;
        err_msg.append("/contacts/find_all 响应错误")
                .append(to_string(res->status))
                .append("(").append(res->reason).append("),错误原因：")
                .append(resp.errmessage());
        throw ContactException(err_msg);    
    }
    else if(!resp.success())
    {            
        string err_msg;
        err_msg.append("/contacts/find_all结果异常： ")       
                .append(resp.errmessage());
        throw ContactException(err_msg);
    }

    //结果打印
    printFindAllContactsResponse(resp);
}

